<!DOCTYPE html>
<html lang="en">
<head>
  <title>Extend EGG - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/basics/extend.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/basics/extend.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Extend EGG</h1>
    <p>Egg.js is extensible and it provides multiple extension points to enhance the functionality of itself:</p>
<ul>
<li>Application</li>
<li>Context</li>
<li>Request</li>
<li>Response</li>
<li>Helper</li>
</ul>
<p>We could use the extension APIs to help us to develop, or extend the objects given above to enhance the functionality of egg as well while programming.</p>
<h2 id="application"><a class="markdown-anchor" href="#application">#</a> Application</h2>
<p>The object <code>app</code> is just the same aspect as the global application object in Koa. There should be only one <code>app</code> in your application, and it will be created by egg when the application is started.</p>
<h3 id="access-method"><a class="markdown-anchor" href="#access-method">#</a> Access Method</h3>
<ul>
<li>
<p><code>ctx.app</code></p>
</li>
<li>
<p>You can access the Application object by using <code>this.app</code> in Controller, Middleware, Helper, Service. For instance, <code>this.app.config</code> will help you access the Config object.</p>
</li>
<li>
<p>The <code>app</code> object would be injected into the entry function as the first argument in <code>app.js</code>, like this:</p>
 <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// here you can use the app object</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="how-to-extend"><a class="markdown-anchor" href="#how-to-extend">#</a> How to extend</h3>
<p>Egg will merge the object defined in <code>app/extend/application.js</code> with the prototype of Application object in Koa, then generate object <code>app</code> which is based on the extended prototype when application is started.</p>
<h4 id="extend-methods"><a class="markdown-anchor" href="#extend-methods">#</a> Extend Methods</h4>
<p>If we want to create a method <code>app.foo()</code>, we can do it like this: ：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/application.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  foo(param) &#123;</span><br><span class="line">    <span class="comment">// `this` points to the object app, you can access other methods or property of app with this</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="extend-properties"><a class="markdown-anchor" href="#extend-properties">#</a> Extend Properties</h4>
<p>Generally speaking, the calculation of properties only need to be done once, therefore we have to do some cache, otherwise it will degrade performance of the app as too much calculation would be going to do when accessing those properties several times.</p>
<p>So, it's recommended to use Symbol + Getter.</p>
<p>For example, if we would like to add a Getter property <code>app.bar</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/application.js</span></span><br><span class="line"><span class="keyword">const</span> BAR = <span class="built_in">Symbol</span>(<span class="string">'Application#bar'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get bar() &#123;</span><br><span class="line">    <span class="comment">// `this` points to the app object, you can access other methods or property of app with this</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>[BAR]) &#123;</span><br><span class="line">      <span class="comment">// It should be more complex in real situation</span></span><br><span class="line">      <span class="keyword">this</span>[BAR] = <span class="keyword">this</span>.config.xx + <span class="keyword">this</span>.config.yy;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>[BAR];</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="context"><a class="markdown-anchor" href="#context">#</a> Context</h2>
<p>Context means the context in Koa, which is a  <strong>Request Level</strong> object. That is to say, every request from client will generate an Context instance. We usually write Context as <code>ctx</code> in short. In all the doc, both Context and <code>ctx</code> means the context object in Koa.</p>
<h3 id="access-method-2"><a class="markdown-anchor" href="#access-method-2">#</a> Access Method</h3>
<ul>
<li><code>this</code> in middleware is ctx, such as <code>this.cookies.get('foo')</code>。</li>
<li>There are two different ways to write controller. If you use class to describe controller, you can use <code>this.ctx</code> to access Context. Or if you write as method, you can access Context with <code>ctx</code> directly.</li>
<li><code>this</code> in helper, service points to the helper object and service object themselves. Simply use <code>this.ctx</code> to access Context object, such as <code>this.ctx.cookies.get('foo')</code>.</li>
</ul>
<h3 id="how-to-extend-2"><a class="markdown-anchor" href="#how-to-extend-2">#</a> How to extend</h3>
<p>Egg will merge the object defined in <code>app/extend/context.js</code> with the prototype of Context object in Koa. And it will generate a <code>ctx</code> object which is based on the extended prototype when deal with request.</p>
<h4 id="extend-methods-2"><a class="markdown-anchor" href="#extend-methods-2">#</a> Extend Methods</h4>
<p>For instance, we could add a method <code>ctx.foo()</code> in the following way:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/context.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  foo(param) &#123;</span><br><span class="line">    <span class="comment">// `this` points to the ctx object, you can access other methods or property of ctx </span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h4 id="extend-properties-2"><a class="markdown-anchor" href="#extend-properties-2">#</a> Extend Properties</h4>
<p>Generally speaking, the calculation of properties only need to do once, therefore we have to do some cache, otherwise it will degrade performance of the app as too much calculation would be going to do when access those properties several times.</p>
<p>So, it's recommended to use Symbol + Getter.</p>
<p>For example, if we would like to add a Getter property <code>ctx.bar</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/context.js</span></span><br><span class="line"><span class="keyword">const</span> BAR = <span class="built_in">Symbol</span>(<span class="string">'Context#bar'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get bar() &#123;</span><br><span class="line">    <span class="comment">// `this` points to the ctx object, you can access other methods or property of ctx</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>[BAR]) &#123;</span><br><span class="line">      <span class="comment">// For example, we can get from header, but it should be more complex in real situation.</span></span><br><span class="line">      <span class="keyword">this</span>[BAR] = <span class="keyword">this</span>.get(<span class="string">'x-bar'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>[BAR];</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="request"><a class="markdown-anchor" href="#request">#</a> Request</h2>
<p>Request object is the same as that in Koa, which is a <strong>Request Level</strong> object. It provides a great number of methods to help to access the properties and methods you need.</p>
<h3 id="access-method-3"><a class="markdown-anchor" href="#access-method-3">#</a> Access Method</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.request</span><br></pre></td></tr></table></figure>
<p>So many properties and methods in <code>ctx</code> can also be accessed in <code>request</code> object. For those properties and methods, it is just the same to access them by using either <code>ctx</code> or <code>request</code>, such as <code>ctx.url === ctx.request.url</code>.</p>
<p>Here are the properties and methods in <code>ctx</code> which can also be accessed by Request aliases: <a href="http://koajs.com/#request-aliases" target="_blank" rel="noopener">Koa - Request aliases</a></p>
<h3 id="how-to-extend-3"><a class="markdown-anchor" href="#how-to-extend-3">#</a> How to extend</h3>
<p>Egg will merge the object defined in <code>app/extend/request.js</code> and the prototype of <code>request</code> object built in egg. And it will generate a <code>request</code> object which is based on the extended prototype when deal with request.</p>
<p>For instance, we could add a property <code>request.foo</code> in the following way:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/request.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get foo() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.get(<span class="string">'x-request-foo'</span>);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="response"><a class="markdown-anchor" href="#response">#</a> Response</h2>
<p>Response object is the same as that in Koa, which is a <strong>Request Level</strong> object. It provides a great number of methods to help to access the properties and methods you need.</p>
<h3 id="access-method-4"><a class="markdown-anchor" href="#access-method-4">#</a> Access Method</h3>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">ctx.response</span><br></pre></td></tr></table></figure>
<p>So many properties and methods in <code>ctx</code> can also be accessed in <code>response</code> object. For those properties and methods, it is just the same to access them by using either <code>ctx</code> or <code>response</code>. For example <code>ctx.status = 404</code> is the same as <code>ctx.response.status = 404</code>.</p>
<p>Here are the properties and methods in <code>ctx</code> which can also be accessed by Response aliases: <a href="http://koajs.com/#response-aliases" target="_blank" rel="noopener">Koa Response aliases</a></p>
<h3 id="how-to-extend-4"><a class="markdown-anchor" href="#how-to-extend-4">#</a> How to extend</h3>
<p>Egg will merge the object defined in <code>app/extend/response.js</code> and the prototype of <code>response</code> object build in egg. And it will generate a <code>response</code> object which is based on the extended prototype after dealt with request.</p>
<p>For instance, we could add a setter <code>request.foo</code> in the following way:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/response.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  set foo(value) &#123;</span><br><span class="line">    <span class="keyword">this</span>.set(<span class="string">'x-response-foo'</span>, value);</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>Then we can use the setter like this: <code>this.response.foo = 'bar';</code></p>
<h2 id="helper"><a class="markdown-anchor" href="#helper">#</a> Helper</h2>
<p>Function Helper can provides some useful utility functions.</p>
<p>We can put some utility functions we use ofter into helper.js as an individual function. Then we can write the complex codes in JavaScript, avoiding to write them everywhere. Besides, such a simple function like Helper allows to write test case much easier.</p>
<p>Egg has had some build-in Helper functions. We can write our own Helper as well.</p>
<h3 id="access-method-5"><a class="markdown-anchor" href="#access-method-5">#</a> Access Method</h3>
<p>Access helper object with <code>ctx.helper</code>, for example:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Assume that home router has already defined in app/router.js</span></span><br><span class="line">app.get(<span class="string">'home'</span>, <span class="string">'/'</span>, <span class="string">'home.index'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use helper to calculate the specific url path</span></span><br><span class="line">ctx.helper.pathFor(<span class="string">'home'</span>, &#123; <span class="attr">by</span>: <span class="string">'recent'</span>, <span class="attr">limit</span>: <span class="number">20</span> &#125;)</span><br><span class="line"><span class="comment">// =&gt; /?by=recent&amp;limit=20</span></span><br></pre></td></tr></table></figure>
<h3 id="how-to-extend-5"><a class="markdown-anchor" href="#how-to-extend-5">#</a> How to extend</h3>
<p>Egg will merge the object defined in <code>app/extend/helper.js</code> and the prototype of <code>helper</code> object build in egg. And it will generate a <code>helper</code> object which is based on the extended prototype after dealt with request.</p>
<p>For instance, we could add a method <code>helper.foo()</code> in the following way:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/helper.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  foo(param) &#123;</span><br><span class="line">    <span class="comment">// // `this` points to the helper object, you can access other methods or property of helper </span></span><br><span class="line">    <span class="comment">// this.ctx =&gt; context object</span></span><br><span class="line">    <span class="comment">// this.app =&gt; application object</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<h2 id="extend-according-to-environment"><a class="markdown-anchor" href="#extend-according-to-environment">#</a> Extend according to environment</h2>
<p>Besides, we can extend the framework in an optional way according to the environment. For example, if you want <code>mockXX()</code> only be able to accessed when doing unittest:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/extend/application.unittest.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  mockXX(k, v) &#123;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>This file will only be required under unittest environment.</p>
<p>Similarly, we could extend egg in this way for other object,such as Application, Context, Request, Response and Helper. See more on <a href="./env.html">environment</a></p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/basics/extend.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/basics/extend.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
